package com.dbflow5.models;

import com.dbflow5.TestDatabase;
import com.dbflow5.annotation.*;
import com.dbflow5.annotation.ManyToMany;
import com.dbflow5.converter.TypeConverters;
import com.dbflow5.data.Blob;
import com.dbflow5.database.DatabaseStatement;
import com.dbflow5.query.SQLiteStatementListener;
import com.dbflow5.structure.BaseModel;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.UUID;

public class SimpleTestModels {
    @Table(database = TestDatabase.class)
    public static class SimpleModel {
        @PrimaryKey
        public String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public SimpleModel(String name) {
            this.name = name;
        }
    }

    @QueryModel(database = TestDatabase.class)
    public static class SimpleCustomModel {
        @Column
        public String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public SimpleCustomModel(String name) {
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class)
    public static class SimpleQuickCheckModel {
        @PrimaryKey(quickCheckAutoIncrement = true, autoincrement = true)
        public int name;

        public int getName() {
            return name;
        }

        public void setName(int name) {
            this.name = name;
        }

        public SimpleQuickCheckModel(int name) {
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class, insertConflict = ConflictAction.FAIL, updateConflict = ConflictAction.FAIL)
    public static class NumberModel {
        @PrimaryKey
        public int id;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public NumberModel(int id) {
            this.id = id;
        }
    }

    @Table(database = TestDatabase.class)
    public static class CharModel {
        @PrimaryKey
        public int id;
        @Column
        public Character exampleChar;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Character getExampleChar() {
            return exampleChar;
        }

        public void setExampleChar(Character exampleChar) {
            this.exampleChar = exampleChar;
        }

        public CharModel(int id, Character exampleChar) {
            this.id = id;
            this.exampleChar = exampleChar;
        }
    }

    @Table(database = TestDatabase.class)
    public static class TwoColumnModel {
        @PrimaryKey
        public String name;
        @Column(defaultValue = "56")
        public int id;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public TwoColumnModel(String name, int id) {
            this.name = name;
            this.id = id;
        }
    }

    @Table(database = TestDatabase.class, createWithDatabase = false)
    public static class DontCreateModel {
        @PrimaryKey
        public int id;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public DontCreateModel(int id) {
            this.id = id;
        }
    }

    public enum Difficulty {
        EASY,
        MEDIUM,
        HARD
    }

    @Table(database = TestDatabase.class)
    public static class EnumModel {
        @PrimaryKey
        public int id;
        @Column
        public Difficulty difficulty;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Difficulty getDifficulty() {
            return difficulty;
        }

        public void setDifficulty(Difficulty difficulty) {
            this.difficulty = difficulty;
        }

        public EnumModel(int id, Difficulty difficulty) {
            this.id = id;
            this.difficulty = difficulty;
        }
    }

    @Table(database = TestDatabase.class, allFields = true)
    public static class AllFieldsModel {
        @PrimaryKey
        public String name;
        public int count;
        @Column(getterName = "getTruth")
        public boolean truth;
        public String fileName;
        @ColumnIgnore
        public int hidden;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

        public boolean isTruth() {
            return truth;
        }

        public void setTruth(boolean truth) {
            this.truth = truth;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public int getHidden() {
            return hidden;
        }

        public void setHidden(int hidden) {
            this.hidden = hidden;
        }

        public static int getCOUNT() {
            return COUNT;
        }

        public AllFieldsModel(String name, int count, boolean truth, String fileName, int hidden) {
            this.name = name;
            this.count = count;
            this.truth = truth;
            this.fileName = fileName;
            this.hidden = hidden;
        }

        public static final int COUNT = 0;
    }

    @Table(database = TestDatabase.class, allFields = true)
    public static class SubclassAllFields extends AllFieldsModel {
        @Column
        public int _order;

        public int getOrder() {
            return _order;
        }

        public void setOrder(int order) {
            this._order = order;
        }

        public SubclassAllFields(int order) {
            super(null, 0, false, "", 0);
            this._order = order;
        }
    }

    @Table(database = TestDatabase.class, assignDefaultValuesFromCursor = false)
    public static class DontAssignDefaultModel {
        @PrimaryKey
        public String name;
        @Column(getterName = "getNullableBool")
        public boolean nullableBool;
        @Column
        public int _index;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public boolean isNullableBool() {
            return nullableBool;
        }

        public void setNullableBool(boolean nullableBool) {
            this.nullableBool = nullableBool;
        }

        public int getIndex() {
            return _index;
        }

        public void setIndex(int index) {
            this._index = index;
        }

        public DontAssignDefaultModel(String name, boolean nullableBool, int index) {
            this.name = name;
            this.nullableBool = nullableBool;
            this._index = index;
        }
    }

    @Table(database = TestDatabase.class, orderedCursorLookUp = true)
    public static class OrderCursorModel {
        @Column
        public int age;
        @PrimaryKey
        public int id;
        @Column
        public String name;

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public OrderCursorModel(int age, int id, String name) {
            this.age = age;
            this.id = id;
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class)
    public static class TypeConverterModel {
        @PrimaryKey
        public int id;
        @Column(typeConverter = BlobConverter.class)
        public byte[] opaqueData;
        @Column
        public Blob blob;
        @Column(typeConverter = CustomTypeConverter.class)
        @PrimaryKey
        public CustomType customType;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public byte[] getOpaqueData() {
            return opaqueData;
        }

        public void setOpaqueData(byte[] opaqueData) {
            this.opaqueData = opaqueData;
        }

        public Blob getBlob() {
            return blob;
        }

        public void setBlob(Blob blob) {
            this.blob = blob;
        }

        public CustomType getCustomType() {
            return customType;
        }

        public void setCustomType(CustomType customType) {
            this.customType = customType;
        }

        public TypeConverterModel(int id, byte[] opaqueData, Blob blob, CustomType customType) {
            this.id = id;
            this.opaqueData = opaqueData;
            this.blob = blob;
            this.customType = customType;
        }
    }

    @Table(database = TestDatabase.class)
    public static class EnumTypeConverterModel {
        @PrimaryKey
        public int id;
        @Column
        public Blob blob;
        @Column
        public byte[] byteArray;
        @Column(typeConverter = CustomEnumTypeConverter.class)
        public Difficulty difficulty;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Blob getBlob() {
            return blob;
        }

        public void setBlob(Blob blob) {
            this.blob = blob;
        }

        public byte[] getByteArray() {
            return byteArray;
        }

        public void setByteArray(byte[] byteArray) {
            this.byteArray = byteArray;
        }

        public Difficulty getDifficulty() {
            return difficulty;
        }

        public void setDifficulty(Difficulty difficulty) {
            this.difficulty = difficulty;
        }

        public EnumTypeConverterModel(int id, Blob blob, byte[] byteArray, Difficulty difficulty) {
            this.id = id;
            this.blob = blob;
            this.byteArray = byteArray;
            this.difficulty = difficulty;
        }
    }

    @Table(database = TestDatabase.class, allFields = true)
    public static class FeedEntry {
        @PrimaryKey
        public int id;
        public String title;
        public String subtitle;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getSubtitle() {
            return subtitle;
        }

        public void setSubtitle(String subtitle) {
            this.subtitle = subtitle;
        }

        public FeedEntry(int id, String title, String subtitle) {
            this.id = id;
            this.title = title;
            this.subtitle = subtitle;
        }
    }

    @Table(database = TestDatabase.class)
    @ManyToMany(
            generatedTableClassName = "Refund", referencedTable = Transfer.class,
            referencedTableColumnName = "refund_in", thisTableColumnName = "refund_out",
            saveForeignKeyModels = true
    )
    public static class Transfer {
        @PrimaryKey
        public UUID transfer_id;

        public UUID getTransfer_id() {
            return transfer_id;
        }

        public void setTransfer_id(UUID transfer_id) {
            this.transfer_id = transfer_id;
        }

        public Transfer(UUID transfer_id) {
            this.transfer_id = transfer_id;
        }
    }

    @Table(database = TestDatabase.class)
    public static class Transfer2 {
        @PrimaryKey
        public UUID id;
        @ForeignKey(stubbedRelationship = true)
        public Account origin;

        public UUID getId() {
            return id;
        }

        public void setId(UUID id) {
            this.id = id;
        }

        public Account getOrigin() {
            return origin;
        }

        public void setOrigin(Account origin) {
            this.origin = origin;
        }

        public Transfer2(UUID id, Account origin) {
            this.id = id;
            this.origin = origin;
        }
    }

    @Table(database = TestDatabase.class)
    public static class Account {
        @PrimaryKey
        public UUID id;

        public UUID getId() {
            return id;
        }

        public void setId(UUID id) {
            this.id = id;
        }

        public Account(UUID id) {
            this.id = id;
        }
    }

    @Table(database = TestDatabase.class)
    public static class SqlListenerModel implements SQLiteStatementListener {
        @PrimaryKey
        public int id;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public SqlListenerModel(int id) {
            this.id = id;
        }

        @Override
        public void onBindToInsertStatement(DatabaseStatement databaseStatement) {

        }

        @Override
        public void onBindToUpdateStatement(DatabaseStatement databaseStatement) {

        }

        @Override
        public void onBindToDeleteStatement(DatabaseStatement databaseStatement) {

        }
    }

    public static class CustomType {
        public int name;

        public int getName() {
            return name;
        }

        public void setName(int name) {
            this.name = name;
        }

        public CustomType(int name) {
            this.name = name;
        }
    }

    public static class CustomTypeConverter extends TypeConverters.TypeConverter<Integer, CustomType> {

        public CustomTypeConverter() {
            super();
        }

        @Override
        public Integer getDBValue(CustomType model) {
            return model != null ? model.name : null;
        }

        @Override
        public CustomType getModelValue(Integer data) {
            if (data == null) {
                return null;
            } else {
                return new CustomType(data);
            }
        }
    }

    public static class CustomEnumTypeConverter extends TypeConverters.TypeConverter<String, Difficulty> {

        public CustomEnumTypeConverter() {
            super();
        }

        @Override
        public String getDBValue(Difficulty model) {
            return model != null ? model.name().substring(0, 1) : null;
        }

        @Override
        public Difficulty getModelValue(String data) {
            switch (data) {
                case "E":
                    return Difficulty.EASY;
                case "M":
                    return Difficulty.MEDIUM;
                case "H":
                    return Difficulty.HARD;
                default:
                    return Difficulty.HARD;
            }
        }
    }

    @TypeConverter
    public static class BlobConverter extends TypeConverters.TypeConverter<Blob, byte[]> {

        public BlobConverter() {
            super();
        }

        @Override
        public Blob getDBValue(byte[] model) {
            return model == null ? null : new Blob(model);
        }

        @Override
        public byte[] getModelValue(Blob data) {
            return new byte[0];
        }
    }

    @Table(database = TestDatabase.class)
    public static class DefaultModel {
        @PrimaryKey
        @Column(defaultValue = "5")
        public int id;
        @Column(defaultValue = "5.0")
        public Double location;
        @Column(defaultValue = "\"String\"")
        public String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Double getLocation() {
            return location;
        }

        public void setLocation(Double location) {
            this.location = location;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public DefaultModel(int id, Double location, String name) {
            this.id = id;
            this.location = location;
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class, cachingEnabled = true)
    public static class TestModelChild extends BaseModel {
        @PrimaryKey
        public long id;
        @Column
        public String name;

        public long getId() {
            return id;
        }

        public void setId(long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public TestModelChild(long id, String name) {
            this.id = id;
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class)
    public static class TestModelParent extends BaseModel {
        @PrimaryKey
        public long id;

        @Column
        public String name;

        @ForeignKey(stubbedRelationship = true)
        public TestModelChild child;

        public long getId() {
            return id;
        }

        public void setId(long id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public TestModelChild getChild() {
            return child;
        }

        public void setChild(TestModelChild child) {
            this.child = child;
        }

        public TestModelParent(long id, String name, TestModelChild child) {
            this.id = id;
            this.name = name;
            this.child = child;
        }
    }

    @Table(database = TestDatabase.class)
    public static class NullableNumbers {
        @PrimaryKey
        public int id;
        @Column
        public Float f;
        @Column
        public Double d;
        @Column
        public long l;
        @Column
        public int i;
        @Column
        public BigDecimal bigDecimal;
        @Column
        public BigInteger bigInteger;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Float getF() {
            return f;
        }

        public void setF(Float f) {
            this.f = f;
        }

        public Double getD() {
            return d;
        }

        public void setD(Double d) {
            this.d = d;
        }

        public long getL() {
            return l;
        }

        public void setL(long l) {
            this.l = l;
        }

        public int getI() {
            return i;
        }

        public void setI(int i) {
            this.i = i;
        }

        public BigDecimal getBigDecimal() {
            return bigDecimal;
        }

        public void setBigDecimal(BigDecimal bigDecimal) {
            this.bigDecimal = bigDecimal;
        }

        public BigInteger getBigInteger() {
            return bigInteger;
        }

        public void setBigInteger(BigInteger bigInteger) {
            this.bigInteger = bigInteger;
        }

        public NullableNumbers(int id, Float f, Double d, long l, int i, BigDecimal bigDecimal, BigInteger bigInteger) {
            this.id = id;
            this.f = f;
            this.d = d;
            this.l = l;
            this.i = i;
            this.bigDecimal = bigDecimal;
            this.bigInteger = bigInteger;
        }
    }

    @Table(database = TestDatabase.class)
    public static class NonNullKotlinModel {
        @PrimaryKey
        public String name;
        @Column
        public Date date;
        @Column
        public int numb;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Date getDate() {
            return date;
        }

        public void setDate(Date date) {
            this.date = date;
        }

        public int getNumb() {
            return numb;
        }

        public void setNumb(int numb) {
            this.numb = numb;
        }

        public NonNullKotlinModel(String name, Date date, int numb) {
            this.name = name;
            this.date = date;
            this.numb = numb;
        }
    }

    @Table(database = TestDatabase.class)
    public static class Owner extends BaseModel {
        @PrimaryKey(autoincrement = true)
        public int id;
        @Column
        public String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Owner(int id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class)
    public static class Dog extends BaseModel {
        @ForeignKey(onDelete = ForeignKeyAction.CASCADE, stubbedRelationship = true)
        public Owner owner;
        @PrimaryKey(autoincrement = true)
        public int id;
        @Column
        public String name;

        public Owner getOwner() {
            return owner;
        }

        public void setOwner(Owner owner) {
            this.owner = owner;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Dog(Owner owner, int id, String name) {
            super();
            this.owner = owner;
            this.id = id;
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class)
    public static class Currency {
        @PrimaryKey(autoincrement = true)
        public long id;
        @Column
        @Unique
        public String symbol;
        @Column
        public String shortName;
        @Column
        @Unique
        public String name;

        public long getId() {
            return id;
        }

        public void setId(long id) {
            this.id = id;
        }

        public String getSymbol() {
            return symbol;
        }

        public void setSymbol(String symbol) {
            this.symbol = symbol;
        }

        public String getShortName() {
            return shortName;
        }

        public void setShortName(String shortName) {
            this.shortName = shortName;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Currency(long id, String symbol, String shortName, String name) {
            this.id = id;
            this.symbol = symbol;
            this.shortName = shortName;
            this.name = name;
        }
    }


    public static class Password {
        public String value;

        public Password(String value) {
            this.value = value;
        }
    }

    public static class Email {
        public String value;

        public Email(String value) {
            this.value = value;
        }
    }

    @Table(database = TestDatabase.class)
    public static class UserInfo {
        @PrimaryKey
        public Email email;
        public Password password;

        public Email getEmail() {
            return email;
        }

        public void setEmail(Email email) {
            this.email = email;
        }

        public Password getPassword() {
            return password;
        }

        public void setPassword(Password password) {
            this.password = password;
        }

        public UserInfo() {
            this(new Email(""), new Password(""));
        }

        public UserInfo(Email email, Password password) {
            this.email = email;
            this.password = password;
        }
    }


    @Table(database = TestDatabase.class)
    public static class InternalClass {
        @PrimaryKey
        public String id;

        public InternalClass(String id) {
            this.id = id;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }
    }

    @Table(database = TestDatabase.class, uniqueColumnGroups = {@UniqueGroup(groupNumber = 1)})
    public static class UniqueModel {
        @PrimaryKey
        public String id;
        @Unique(uniqueGroups = {1})
        public String name;
        @ForeignKey
        @Unique(uniqueGroups = {1})
        public TypeConverterModel model;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public TypeConverterModel getModel() {
            return model;
        }

        public void setModel(TypeConverterModel model) {
            this.model = model;
        }

        public UniqueModel(String id, String name, TypeConverterModel model) {
            this.id = id;
            this.name = name;
            this.model = model;
        }
    }

    @Table(database = TestDatabase.class)
    @Fts3
    public static class Fts3Model {
        public String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Fts3Model(String name) {
            this.name = name;
        }
    }

    @Table(database = TestDatabase.class)
    public static class Fts4Model {
        @PrimaryKey(autoincrement = true)
        public int id;
        public String name;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Fts4Model(int id, String name) {
            this.id = id;
            this.name = name;
        }

    }

    @Table(database = TestDatabase.class)
    @Fts4(contentTable = Fts4Model.class)
    public static class Fts4VirtualModel2 {
        public String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Fts4VirtualModel2(String name) {
            this.name = name;
        }
    }
}
